package com.artnology.independence.model;

import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.artnology.independence.model.relationships.DVReference;

public class DVGraphModel {
	ConcurrentMap<String, DVType> allTypesMap = null;
	ConcurrentMap<String, DVType> allPhantomTypesMap = null;
	ConcurrentMap<String, DVPackage> allPackages = null;
	DependencyGraph myGraph = null;
	DVRootPackage rootPackage = null;
	ConcurrentMap<DVNode, Set<DVReference>> incomingReferencesMap = new ConcurrentHashMap<DVNode, Set<DVReference>>();

	public DVGraphModel() {
		super();
		this.allTypesMap = new ConcurrentHashMap<String, DVType>();
		this.allPhantomTypesMap = new ConcurrentHashMap<String, DVType>();
		this.allPackages = new ConcurrentHashMap<String, DVPackage>();
		rootPackage = new DVRootPackage(this);
	}

	public Collection<DVType> getAllTypes() {
		return allTypesMap.values();
	}

	public Collection<DVType> getAllPhantomTypes() {
		return allPhantomTypesMap.values();
	}

	public DVRootPackage getRootPackage() {
		return rootPackage;
	}

	public ConcurrentMap<DVNode, Set<DVReference>> getIncomingReferencesMap() {
		return incomingReferencesMap;
	}

	public Collection<DVPackage> getAllPackages() {
		List<DVPackage> sortedPackageList = new ArrayList<DVPackage>(
				allPackages.values());
		Collections.sort(sortedPackageList, new Comparator<DVPackage>() {

			@Override
			public int compare(DVPackage o1, DVPackage o2) {
				if (o1.getTokenizedNames().length < o2.getTokenizedNames().length) {
					return 1;
				} else if (o1.getTokenizedNames().length > o2
						.getTokenizedNames().length) {
					return -1;
				} else {
					return o1.getName().compareTo(o2.getName());
				}
			}
		});
		return sortedPackageList;
	}

	public DependencyGraph getMyGraph() {
		return myGraph;
	}

	public void setMyGraph(DependencyGraph myGraph) {
		this.myGraph = myGraph;
	}

	public DVPackage getPackage(String packageName) {
		return allPackages.get(packageName);
	}

	public boolean addType(DVType type) {
		// first check whether new type candidate is already known as phantom
		// type
		if (allPhantomTypesMap.containsKey(type.getFullyQualifiedName())) {
			type = allPhantomTypesMap.remove(type.getFullyQualifiedName());
		}

		if (allTypesMap.putIfAbsent(type.getFullyQualifiedName(), type) == null) {
			DVPackage newPack = new DVPackage(type.getPackageName(), this);
			DVPackage theTypesPackage = allPackages.putIfAbsent(
					type.getPackageName(), newPack);
			if (theTypesPackage == null) {
				List<Color> rootColors = generateColors(getAllPackages().size() + 1);
				newPack.setPackageColor(rootColors.get(rootColors.size() - 1));
				newPack.addContainedType(type);
			} else {
				theTypesPackage.addContainedType(type);
			}
			return true;
		}
		return false;
	}

	public DVType addPhantomType(DVType type) {
		return allPhantomTypesMap.putIfAbsent(type.getFullyQualifiedName(),
				type);
	}

	public Set<DVPackage> getTopLevelPackages() {
		Set<DVPackage> topLevelPackages = new HashSet<DVPackage>();
		for (DVPackage onePackage : getAllPackages()) {
			if (onePackage.isTopLevelPackage()) {
				topLevelPackages.add(onePackage);
			}
		}
		return topLevelPackages;
	}

	private final void initializePackagesAndDependencies() {

		// // now calculate and setup all package dependencies
		// for (DVPackage onePackage : graphModel.getAllPackages()) {
		//
		// Map<DVPackage, DVPackageRelationship> packDependencyMap = new
		// HashMap<DVPackage, DVPackageRelationship>();
		// for (DVNode myType : onePackage.getContainedTypes()) {
		// for (DVReference reference : myType.getReferencesTo()) {
		// if (!onePackage.isContained((DVType) reference
		// .getReferencedNode())) {
		// // if the referenced type is not contained in this
		// // package
		// // we will setup a package dependency to its package
		// DVPackage referencedPackage = ((DVType) reference
		// .getReferencedNode()).getPackage();
		// if (packDependencyMap.containsKey(referencedPackage)) {
		// packDependencyMap.get(referencedPackage)
		// .increaseWeight();
		// } else {
		// DVPackageRelationship newPackDependency = new DVPackageRelationship(
		// Activator.getDefault().getModel(),
		// referencedPackage, onePackage);
		// packDependencyMap.put(referencedPackage,
		// newPackDependency);
		// }
		//
		// }
		// }
		// }
		// for (DVReference pd : packDependencyMap.values()) {
		// onePackage.addReferenceTo(pd);
		// }
		// }
	}

	private static final void setChildrenPackageColors(DVPackage parentPackage) {
		List<Color> childrenColors = generateColors(parentPackage.getChildren()
				.size());
		int i = 0;
		for (DVPackage childPackage : parentPackage.getChildren()) {
			childPackage.setPackageColor(childrenColors.get(i));
			setChildrenPackageColors(childPackage);
			i++;
		}
	}

	private static final List<Color> generateColors(int n) {
		Color[] cols = new Color[n];
		for (int i = 0; i < n; i++) {
			cols[i] = Color.getHSBColor((float) i / (float) n, 0.85f, 0.85f);
		}
		List<Color> colors = new ArrayList<Color>();
		for (int i = 0; i < cols.length; i++) {
			colors.add(cols[i]);
		}
		return colors;
	}

	public boolean containsTypeFQ(String fullyQualifiedTypeName) {
		return allTypesMap.containsKey(fullyQualifiedTypeName);
	}

	public DVType getTypeForFQName(String fullyQualifiedTypeName) {
		return allTypesMap.get(fullyQualifiedTypeName);
	}

	public DVType getTypeForSimpleName(String simpleTypeName) {
		for (DVType type : getAllTypes()) {
			if (type.getName().equals(simpleTypeName)) {
				return type;
			}
		}
		return null;
	}

	public DVType getTypeForSimpleNameIgnoreCase(String simpleTypeName) {
		for (DVType type : getAllTypes()) {
			if (type.getName().equalsIgnoreCase(simpleTypeName)) {
				return type;
			}
		}
		return null;
	}

	public boolean containsTypeSimpleName(String simpleTypeName) {
		int ambigiousTypesCounter = 0;
		for (DVModelObject type : getAllTypes()) {
			if (type.getName().equals(simpleTypeName)) {
				ambigiousTypesCounter++;
			}
		}
		if (ambigiousTypesCounter > 1) {
			System.err
					.println("Warning!! containsTypeSimpleName yielded more than one matching type for simpleTypeName "
							+ simpleTypeName);
			return true;
		} else if (ambigiousTypesCounter == 1) {
			return true;
		} else {
			return false;
		}
	}

	public Collection<DVNode> getAllVisibleNodes() {
		return getMyGraph().getGraph().getVertices();
	}

	public Collection<DVNode> getAllNonCrossPackageTypes() {
		Collection<DVNode> nonCrossPackageTypes = new HashSet<DVNode>();
		for (DVNode type : getAllTypes()) {
			if (!type.hasCrossPackageReference()) {
				nonCrossPackageTypes.add(type);
			}
		}
		return nonCrossPackageTypes;
	}

	public void listTypesInAllPackages() {
		List<DVPackage> sortedPackages = new ArrayList<DVPackage>(
				getAllPackages());
		Collections.sort(sortedPackages, new Comparator<DVPackage>() {

			@Override
			public int compare(DVPackage o1, DVPackage o2) {
				if (o1.getNumberOfContainedTypesWithSubpackages() > o2
						.getNumberOfContainedTypesWithSubpackages()) {
					return 1;
				} else if (o1.getNumberOfContainedTypesWithSubpackages() > o2
						.getNumberOfContainedTypesWithSubpackages()) {
					return -1;
				} else {
					return 0;
				}
			}
		});
		for (DVPackage dvPackage : sortedPackages) {
			System.out.println(dvPackage.getName() + " #"
					+ dvPackage.getNumberOfContainedTypesWithSubpackages());
			for (DVPackage child : dvPackage.getChildren()) {
				System.out.println("--- " + child.getName() + " #"
						+ child.getNumberOfContainedTypesWithSubpackages());
			}
		}
	}

}
